\chapter{Prototypes et Idées}
\paragraph{}La prise en main de CUDA nous a demandé un temps non négligeable. C'est pourquoi au rendu du rapport préliminaire, nous n'avions pas encore de prototype implémenté. Néanmoins, voici les bases que nous avions pour optique de suivre avant de commencer l'implémentation complète.
\section{Méthode d'appel générique}
\paragraph{}Le but est ici d'avoir une approche extérieur du système la plus simple possible pour l'utilisateur tout en garantissant les résultats optimaux.
\subsection*{Simplicité d'appel}
\paragraph{}La création du moteur de convolution ne doit demander que le strict minimum à l'utilisateur (c'est à dire sa matrice et son entrée) et de recompiler son programme sur la machine de traitement.
\paragraph{}Typiquement, on ne devrait pas avoir plus que cela dans le code :\\
\newline
\begin{Verbatim}
     ConvolEngine engine(matrice);
     engine.convol(entree);
\end{Verbatim}

\subsection*{Choix d'architecture caché mais présent}
\paragraph{}Si l'utilisateur ne souhaite pas s'occuper du choix de son architecture, le choix optimal pour tirer parti de la puissance disponible sur la machine devra être fait à la compilation. Cependant pour l'utilisateur averti, il devra être possible d'affiner ses choix.
\paragraph{}Pour cela nous avons pensé à un appel template de ce type :\\
\newline
\begin{Verbatim}
    ConvolEngine<CUDA> engine(matrice);
    \\ ConvolEngine<MULTITHREAD> engine(matrice)
    engine.convol(entree);
\end{Verbatim}

\subsection*{Méthode d'action similaire}
\paragraph{}Même s'il doit être possible d'agir sur un seul processeur en simple ou multi-thread ou sur une carte graphique, l'approche de fonctionnement doit rester la même pour éviter au maximum de produire du code trop différent entre architectures. De plus cela nous permettra de tester la validité d'une méthode réellement générique. L'approche dont il est question sera un fonctionnement du traitement par bloc.
\section{Mémoire : partage et découpage}
\paragraph{}L'utilisation de la mémoire est très différente selon l'approche CPU ou GPU d'un système. Néanmoins, de par la nature du problème à traiter nous avons pu cerner des similarités importantes entre ces deux architectures pour contourner certaines particularités.
\subsection*{Fonctionnement par bloc}
\paragraph{}Une architecture massivement parallèle nous oblige à trouver un découpage efficace des données afin de limiter les transferts mémoire. En effet si le nombre d'unité de traitement sur une carte graphique est généralement très élevée, la mémoire disponible sur ceux-ci est généralement assez faible.
\paragraph{}L'approche la plus logique est ici d'attaquer le problème par bloc de manière à ce que ceux ci utilisent cette mémoire de manière optimale. l'idée serait alors la suivante :\\
\newline
\begin{algorithm}[H]
\Repeter{plus de blocs à traiter}{
  Distribuer les blocs à toutes les unités\\
  Traiter localement les blocs\\
  Récupérer les résultats de chacun\\
}
\end{algorithm}
\paragraph{}L'avantage de ce fonctionnement est qu'il reste pertinent en simple processeur simple thread ou multi-thread, le nombre de blocs sera juste largement inférieur et ils seront de taille supérieure du fait de la mémoire disponible plus importante.
\subsection*{Entrée découpée en apparence}
\paragraph{}Les blocs de données à traiter lors d'une convolution sont indépendants les uns des autres (modulo la gestion du bord de ces blocs). De plus les actions effectuées ne doivent pas altérer l'entrée (le résultat est écrit dans la matrice de sortie différente de l'entrée), de cette manière on constate que le travail des différentes unités ne rentre pas en conflit.
\paragraph{}Sur un calcul sur CPU, la matrice sur laquelle on travaille sera sur la même mémoire et accessible par tous les fils d'exécution. La manière similaire d'effectuer cela (tout en restant rapide !) sur une architecture GPU est de stocker cette matrice d'entrée dans la mémoire réservée aux textures (car c'est finalement sur une texture que l'on agit), de cette manière tous les processeurs graphiques auront accès à l'entrée de manière rapide et sans nécessiter de transfert autre que le chargement de l'entrée dans la mémoire graphique.
\paragraph{}Le découpage de l'entrée entre les différentes unités n'est alors qu'une apparence : on ne définit pour chaque unité que son champ d'action.
\subsection*{Sortie découpée}
\paragraph{}En sortie, en revanche, les choses sont légèrement différentes. En effet, chaque unité de calcul de la carte graphique devra garder en mémoire sa sortie pendant la durée de son traitement, puis la transmettre à la mémoire partagée totale pour avoir de nouveau un espace suffisant pour commencer un nouveau traitement.
\paragraph{}De ce point de vue le traitement sera assez différent entre les deux architectures car le travail sur GPU nécessitera des opérations explicites de transfert et de rassemblement des données qui n'est pas obligatoire sur architecture CPU où la mémoire de sortie est elle aussi entièrement partagée. Néanmoins le code ne diffèrera que sur quelques lignes.
\section{Tests et optimisations possibles}
\paragraph{}Notre approche par bloc nous donne certains angles de comparaison et d'optimisation. Il est probable que ces différents points ne soient pas traités durant la durée du projet mais ils offrent des possibilités intéressantes.
\subsection*{Méthode par blocs et ``naïve''}
\paragraph{}Notre approche du problème semble aussi intéressante, que le traitement s'effectue de manière séquentielle ou (massivement) multi-thread. Il serait intéressant de comparer l'exécution séquentielle de nos algorithmes avec et sans l'approche par bloc afin de voir si elle ne souffre pas de défauts qui ne nous seraient pas apparus au premier abord.
\subsection*{Gestion mémoire approfondie}
\paragraph{}Il serait également intéressant de voir s'il est possible, en optimisant les chargements mémoire et en affinant au mieux notre manière d'appliquer la convolution, de gagner de la vitesse. En effet certains paramètres comme la proportion d'utilisation de la mémoire des CUDA cores\footnote{C{oe}urs CUDA} pour le fonctionnement côté hardware (driver, autre ?) ne sont pas précisés dans la documentation {\Large \textsc{nVidia}}, il pourrait être utile de les cerner le plus précisément possible.

\newpage

\subsection*{Les bases d'une méthode hybride}
\paragraph{}Enfin, nous pourrions poser sur le papier quelques idées précises de ce que pourrait être le fonctionnement de notre bibliothèque sur un système qui tirerait parti à la fois de son (ses) processeur(s) et de son GPU afin de mobiliser vraiment toutes les ressources d'un système pour la résolution d'un problème.

\paragraph{}Les mots d'ordres à retenir pour la suite de ce travail sont donc : simplicité, efficacité et performance.
\section{Evolution par rapport au prototype}
\paragraph{}Il est important de noter que ces prototypes n'étaient qu'une base de travail, les tests et recherches faits par la suite nous en ont beaucoup éloignés.
\paragraph{}Certaines classes minimalistes ont été étoffées, d'autres se sont vues simplifiées. Les syntaxes d'appels ont été, pour la plupart des cas, fortements revues. La partie Architecture et Documentation représente le véritable résultat obtenu.
